local unpack = unpack or table.unpack
-- local colorscheme = 'kanagawa-wave'
local colorscheme = 'everforest'
local file_explorer = os.getenv('NVIM_FILE_EXPLORER') or 'xplr'
local vim = vim or {} -- LSP please STFU
local offset = 4
local enable_mason = os.getenv('NVIM_ENABLE_MASON')=='true' -- For non-Nixers

vim.opt.clipboard:append{'unnamed', 'unnamedplus'}
vim.opt.colorcolumn:append{'80', '120'}
local trueopts = {'cul', 'nu', 'rnu', 'list', 'expandtab', 'termguicolors'}
for _, opt in ipairs(trueopts) do vim.opt[opt] = true end
vim.opt.wrap = false
vim.opt.tabstop = offset; vim.opt.shiftwidth = offset

local lazypath = vim.fn.stdpath('data')..'/lazy/lazy.nvim'
if not vim.loop.fs_stat(lazypath) then
    print('Installing lazy.nvim...')
    vim.fn.system({'git', 'clone', '--filter=blob:none',
        'https://github.com/folke/lazy.nvim.git', '--branch=stable', lazypath})
end
vim.opt.rtp:prepend(lazypath)

local mini = {align={}, animate={}, bracketed={}, comment={},
    hipatterns={}, indentscope={}, jump2d={}, map={}, pairs={}, splitjoin={},
    surround={}}

local plugins = {
    {'nvim-lua/plenary.nvim'},
    {'L3MON4D3/LuaSnip'},
    {'VonHeikemen/lsp-zero.nvim', branch = 'v3.x'},
    {'hrsh7th/cmp-nvim-lsp'},
    {'hrsh7th/cmp-nvim-lua'},
    {'hrsh7th/nvim-cmp'},
    {'hrsh7th/cmp-cmdline'},
    {'FelipeLema/cmp-async-path'},
    {'dmitmel/cmp-digraphs'},
    {'hrsh7th/cmp-buffer'},
    {'PhilRunninger/cmp-rpncalc'},
    {'neovim/nvim-lspconfig'},
    {'sainnhe/everforest'},
    {'voldikss/vim-floaterm'},
    {'nvim-telescope/telescope.nvim'},
    {'nvim-lualine/lualine.nvim'},
    {'yamatsum/nvim-cursorline'},
    {'nanozuki/tabby.nvim'},
    {'NMAC427/guess-indent.nvim'},
}

vim.g.floaterm_opener = 'edit'
vim.g.floaterm_width = 0.85
vim.g.floaterm_height = 0.85

local function add_plugins(list)
    for _, plugin in ipairs(list) do table.insert(plugins, plugin) end
end

if enable_mason then add_plugins {
    {'williamboman/mason-lspconfig.nvim'},
    {'williamboman/mason.nvim'},
} end

add_plugins((function(specs)
    for k in pairs(mini) do specs[#specs+1] = {'echasnovski/mini.'..k} end
    return specs
end){})

require('lazy').setup(plugins)

local everforest_config = {background = 'hard', enable_italic = true}
for k, v in pairs(everforest_config) do vim.g['everforest_'..k] = v end
vim.cmd('colorscheme '..colorscheme)

local lsp_zero = require 'lsp-zero'

lsp_zero.on_attach(function(client, bufnr)
    lsp_zero.default_keymaps{buffer = bufnr}
end)

if enable_mason then
    require('mason').setup{}
    require('mason-lspconfig').setup{handlers = {lsp_zero.default_setup}}
end

local has_words_before = function()
    unpack = unpack or table.unpack
    local line, col = unpack(vim.api.nvim_win_get_cursor(0))
    return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]
        :sub(col, col):match("%s") == nil
end

local cmp, luasnip = require 'cmp', require 'luasnip'

cmp.setup{
    mapping = cmp.mapping.preset.insert{
        ['<Tab>'] = cmp.mapping(function(fallback)
            if cmp.visible() then cmp.select_next_item()
            elseif luasnip.expand_or_jumpable() then luasnip.expand_or_jump()
            elseif has_words_before() then cmp.complete()
            else fallback() end
        end, {'i', 's'}),
        ['<S-Tab>'] = cmp.mapping(function(fallback)
            if cmp.visible() then cmp.select_prev_item()
            elseif luasnip.jumpable(-1) then luasnip.jump(-1)
            else fallback() end
        end, {'i', 's'}),
    },
    sources = {
        { name = 'nvim_lsp' },
        { name = 'nvim_lua' },
        { name = 'async_path' },
        { name = 'buffer' },
        { name = 'digraphs' },
        { name = 'rpncalc' },
    },
}

cmp.setup.cmdline('/', {
    mapping = cmp.mapping.preset.cmdline(),
    sources = { { name = 'buffer' } }
})

cmp.setup.cmdline(':', {
    mapping = cmp.mapping.preset.cmdline(),
    sources = { { name = 'path' }, {
        name = 'cmdline',
        option = { ignore_cmds = { 'Man', '!' } },
    } },
})

if not enable_mason then
    local lspconfig = require 'lspconfig'
    local lsp_servers = {bashls={}, clangd={}, cssls={}, lua_ls={}, nixd={},
        pyright={}, rust_analyzer={}, tsserver={}}
    for name, cfg in pairs(lsp_servers) do lspconfig[name].setup(cfg) end
end

for mod, opts in pairs(mini) do require('mini.'..mod).setup(opts) end

local telescope_builtin = require 'telescope.builtin'

vim.g.mapleader = ';'
local keymaps = {
    {'i', '<leader><leader>', '<C-v><leader>'},
    {'n', '<leader>q', ':q<CR>'},
    {'n', '<leader>w', '<C-w>'},
    {'i', 'jj', '<ESC>'},
    {'i', 'j<leader>', '<C-v>j'},
    {'n', 'ww', ':w<CR>'},
    {'n', '<leader>fr',  telescope_builtin.lsp_references, {}},
    {'n', '<leader>fci', telescope_builtin.lsp_incoming_calls, {}},
    {'n', '<leader>fsd', telescope_builtin.lsp_document_symbols, {}},
    {'n', '<leader>fsw', telescope_builtin.lsp_workspace_symbols, {}},
    {'n', '<leader>ff', telescope_builtin.find_files, {}},
    {'n', '<leader>fg', telescope_builtin.live_grep, {}},
    {'n', '<leader>fb', telescope_builtin.buffers, {}},
    {'n', '<leader>fh', telescope_builtin.help_tags, {}},
    {'n', '<leader>t', ":FloatermNew --title=(SHELL) <CR>"},
    {'n', '<leader>m', ":FloatermNew --title=(FS) "..file_explorer..'<CR>'},
    {'n', '<leader>d', vim.lsp.buf.hover, {}},
    {'n', '<leader>rn', vim.lsp.buf.rename, {}},
    {'n', 'gd', vim.lsp.buf.definition, {}},
    {'n', 'gi', vim.lsp.buf.implementation, {}},
    {'n', '<leader>n', ':bn<CR>'},
    {'n', '<leader>p', ':bp<CR>'},
    {'n', '<leader>ac', vim.lsp.buf.code_action, {}},
    {'t', '<leader>gn', '<C-\\><C-n>'},
} for _, spec in ipairs(keymaps) do vim.keymap.set(unpack(spec)) end

require('lualine').setup{
    options = {
        icons_enabled = false, theme = 'everforest',
        component_separators = {left='', right=''},
        section_separators = {left='', right=''},
        disabled_filetypes = {statusline = {}, winbar = {}},
        ignore_focus = {}, always_divide_middle = true,
        globalstatus = false,
        refresh = {statusline = 1000, tabline = 1000, winbar = 1000},
    },
    sections = {
        lualine_a = {'mode'},
        lualine_b = {'branch', 'diff', 'diagnostics'},
        lualine_c = {'filename'},
        lualine_x = {'encoding', 'fileformat', 'filetype'},
        lualine_y = {'progress'},
        lualine_z = {'location'},
    },
    inactive_sections = {
        lualine_a = {},
        lualine_b = {},
        lualine_c = {'filename'},
        lualine_x = {'location'},
        lualine_y = {},
        lualine_z = {}
    },
    tabline = {},
    winbar = {},
    inactive_winbar = {},
    extensions = {}
}

require('nvim-cursorline').setup{
    cursorline = {enable = true, timeout = 250, number = false},
    cursorword = {enable = false}
}

do
    local theme = {fill = 'TabLineFill', head = 'TabLine', tab = 'TabLine',
        current_tab = 'TabLineSel', win = 'TabLine', tail = 'TabLine',
    }
    require('tabby.tabline').set(function(line)
        local _left = {[false]='', [true]='('}
        local _right = {[false]='', [true]=')'}
        local function surround(el, cond)
            return _left[cond]..tostring(el).._right[cond]
        end
        return {
            line.tabs().foreach(function(tab)
                local hl = tab.is_current() and theme.current_tab or theme.tab
                return {' ', surround(tab.number(), tab.is_current()), ' ',
                    tab.name(), ' ', hl = hl}
            end),
            line.spacer(),
            line.wins_in_tab(line.api.get_current_tab()).foreach(function(win)
                local hl = win.is_current() and theme.current_tab or theme.win
                return {' ', surround(win.buf_name(), win.is_current()), ' ',
                    hl = hl}
            end),
            hl = theme.fill,
        }
    end)
end

require('guess-indent').setup {
    auto_cmd = true,
    override_editorconfig = false,
    filetype_exclude = {
        "netrw",
        "tutor",
    },
    buftype_exclude = {
        "help",
        "nofile",
        "terminal",
        "prompt",
    },
}
